Conditions | 43 |
Paths | 13464 |
Total Lines | 206 |
Lines | 0 |
Ratio | 0 % |
Changes | 10 | ||
Bugs | 2 | Features | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like wallet_sweeper.js ➔ WalletSweeper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | var UnspentOutputFinder = require('./unspent_output_finder'); |
||
20 | var WalletSweeper = function(backupData, bitcoinDataClient, options) { |
||
21 | /* jshint -W071, -W074 */ |
||
22 | var self = this; |
||
23 | this.defaultSettings = { |
||
24 | network: 'btc', |
||
25 | testnet: false, |
||
26 | logging: false, |
||
27 | bitcoinCash: false, |
||
28 | sweepBatchSize: 200 |
||
29 | }; |
||
30 | this.settings = _.merge({}, this.defaultSettings, options); |
||
31 | this.bitcoinDataClient = bitcoinDataClient; |
||
32 | this.utxoFinder = new UnspentOutputFinder(bitcoinDataClient, this.settings); |
||
33 | this.sweepData = null; |
||
34 | |||
35 | // set the bitcoinlib network |
||
36 | if (typeof options.network === "object") { |
||
37 | this.network = options.network; |
||
38 | } else { |
||
39 | this.network = this.getBitcoinNetwork(this.settings.network, this.settings.testnet); |
||
40 | } |
||
41 | |||
42 | backupData.walletVersion = backupData.walletVersion || 2; //default to version 2 wallets |
||
43 | |||
44 | var usePassword = false; |
||
45 | |||
46 | // validate backup data, cleanup input, and prepare seeds |
||
47 | if (!Array.isArray(backupData.blocktrailKeys)) { |
||
48 | throw new Error('blocktrail pub keys are required (must be type Array)'); |
||
49 | } |
||
50 | |||
51 | switch (backupData.walletVersion) { |
||
52 | case 1: |
||
53 | if (typeof backupData.primaryMnemonic === "undefined" || !backupData.primaryMnemonic) { |
||
54 | throw new Error('missing primary mnemonic for version 1 wallet'); |
||
55 | } |
||
56 | if (typeof backupData.backupMnemonic === "undefined" || !backupData.backupMnemonic) { |
||
57 | throw new Error('missing backup mnemonic for version 1 wallet'); |
||
58 | } |
||
59 | if (typeof backupData.primaryPassphrase === "undefined") { |
||
60 | throw new Error('missing primary passphrase for version 1 wallet'); |
||
61 | } |
||
62 | |||
63 | // cleanup copy paste errors from mnemonics |
||
64 | backupData.primaryMnemonic = backupData.primaryMnemonic.trim() |
||
65 | .replace(new RegExp("\r\n", 'g'), " ") |
||
66 | .replace(new RegExp("\n", 'g'), " ") |
||
67 | .replace(/\s+/g, " "); |
||
68 | backupData.backupMnemonic = backupData.backupMnemonic.trim() |
||
69 | .replace(new RegExp("\r\n", 'g'), " ") |
||
70 | .replace(new RegExp("\n", 'g'), " ") |
||
71 | .replace(/\s+/g, " "); |
||
72 | break; |
||
73 | |||
74 | case 2: |
||
75 | case 3: |
||
76 | if (typeof backupData.encryptedPrimaryMnemonic === "undefined" || !backupData.encryptedPrimaryMnemonic) { |
||
77 | throw new Error('missing encrypted primary seed for version 2 wallet'); |
||
78 | } |
||
79 | if (typeof backupData.backupMnemonic === "undefined" || (!backupData.backupMnemonic && backupData.backupMnemonic !== false)) { |
||
80 | throw new Error('missing backup seed for version 2 wallet'); |
||
81 | } |
||
82 | //can either recover with password and password encrypted secret, or with encrypted recovery secret and a decryption key |
||
83 | usePassword = typeof backupData.password !== "undefined" && backupData.password !== null; |
||
84 | if (usePassword) { |
||
85 | if (typeof backupData.passwordEncryptedSecretMnemonic === "undefined" || !backupData.passwordEncryptedSecretMnemonic) { |
||
86 | throw new Error('missing password encrypted secret for version 2 wallet'); |
||
87 | } |
||
88 | if (typeof backupData.password === "undefined") { |
||
89 | throw new Error('missing primary passphrase for version 2 wallet'); |
||
90 | } |
||
91 | } else { |
||
92 | if (typeof backupData.encryptedRecoverySecretMnemonic === "undefined" || !backupData.encryptedRecoverySecretMnemonic) { |
||
93 | throw new Error('missing encrypted recovery secret for version 2 wallet (recovery without password)'); |
||
94 | } |
||
95 | if (!backupData.recoverySecretDecryptionKey) { |
||
96 | throw new Error('missing recovery secret decryption key for version 2 wallet (recovery without password)'); |
||
97 | } |
||
98 | } |
||
99 | |||
100 | // cleanup copy paste errors from mnemonics |
||
101 | backupData.encryptedPrimaryMnemonic = backupData.encryptedPrimaryMnemonic.trim() |
||
102 | .replace(new RegExp("\r\n", 'g'), " ") |
||
103 | .replace(new RegExp("\n", 'g'), " ") |
||
104 | .replace(/\s+/g, " "); |
||
105 | backupData.backupMnemonic = (backupData.backupMnemonic || "").trim() |
||
106 | .replace(new RegExp("\r\n", 'g'), " ") |
||
107 | .replace(new RegExp("\n", 'g'), " ") |
||
108 | .replace(/\s+/g, " "); |
||
109 | if (backupData.recoverySecretDecryptionKey) { |
||
110 | backupData.recoverySecretDecryptionKey = backupData.recoverySecretDecryptionKey.trim() |
||
111 | .replace(new RegExp("\r\n", 'g'), " ") |
||
112 | .replace(new RegExp("\n", 'g'), " ") |
||
113 | .replace(/\s+/g, " "); |
||
114 | } |
||
115 | if (usePassword) { |
||
116 | backupData.passwordEncryptedSecretMnemonic = backupData.passwordEncryptedSecretMnemonic.trim() |
||
117 | .replace(new RegExp("\r\n", 'g'), " ").replace(new RegExp("\n", 'g'), " ").replace(/\s+/g, " "); |
||
118 | } else { |
||
119 | backupData.encryptedRecoverySecretMnemonic = backupData.encryptedRecoverySecretMnemonic.trim() |
||
120 | .replace(new RegExp("\r\n", 'g'), " ").replace(new RegExp("\n", 'g'), " ").replace(/\s+/g, " "); |
||
121 | } |
||
122 | |||
123 | break; |
||
124 | |||
125 | default: |
||
126 | throw new Error('Wrong version [' + backupData.walletVersion + ']'); |
||
127 | } |
||
128 | |||
129 | |||
130 | // create BIP32 HDNodes for the Blocktrail public keys |
||
131 | this.blocktrailPublicKeys = {}; |
||
132 | _.each(backupData.blocktrailKeys, function(blocktrailKey) { |
||
133 | self.blocktrailPublicKeys[blocktrailKey['keyIndex']] = bitcoin.HDNode.fromBase58(blocktrailKey['pubkey'], self.network); |
||
134 | }); |
||
135 | |||
136 | // convert the primary and backup mnemonics to seeds (using BIP39) |
||
137 | var primarySeed, backupSeed, secret; |
||
138 | switch (backupData.walletVersion) { |
||
139 | case 1: |
||
140 | primarySeed = bip39.mnemonicToSeed(backupData.primaryMnemonic, backupData.primaryPassphrase); |
||
141 | backupSeed = bip39.mnemonicToSeed(backupData.backupMnemonic, ""); |
||
142 | break; |
||
143 | |||
144 | case 2: |
||
145 | // convert mnemonics to hex (bip39) and then base64 for decryption |
||
146 | backupData.encryptedPrimaryMnemonic = blocktrail.convert(bip39.mnemonicToEntropy(backupData.encryptedPrimaryMnemonic), 'hex', 'base64'); |
||
147 | if (usePassword) { |
||
148 | backupData.passwordEncryptedSecretMnemonic = blocktrail.convert( |
||
149 | bip39.mnemonicToEntropy(backupData.passwordEncryptedSecretMnemonic), 'hex', 'base64'); |
||
150 | } else { |
||
151 | backupData.encryptedRecoverySecretMnemonic = blocktrail.convert( |
||
152 | bip39.mnemonicToEntropy(backupData.encryptedRecoverySecretMnemonic), 'hex', 'base64'); |
||
153 | } |
||
154 | |||
155 | // decrypt encryption secret |
||
156 | if (usePassword) { |
||
157 | secret = CryptoJS.AES.decrypt(backupData.passwordEncryptedSecretMnemonic, backupData.password).toString(CryptoJS.enc.Utf8); |
||
158 | } else { |
||
159 | secret = CryptoJS.AES.decrypt(backupData.encryptedRecoverySecretMnemonic, backupData.recoverySecretDecryptionKey).toString(CryptoJS.enc.Utf8); |
||
160 | } |
||
161 | |||
162 | if (!secret) { |
||
163 | throw new Error("Could not decrypt secret with " + (usePassword ? "password" : "decryption key")); |
||
164 | } |
||
165 | |||
166 | // now finally decrypt the primary seed and convert to buffer (along with backup seed) |
||
167 | primarySeed = new Buffer(CryptoJS.AES.decrypt(backupData.encryptedPrimaryMnemonic, secret).toString(CryptoJS.enc.Utf8), 'base64'); |
||
|
|||
168 | |||
169 | if (backupData.backupMnemonic) { |
||
170 | backupSeed = new Buffer(bip39.mnemonicToEntropy(backupData.backupMnemonic), 'hex'); |
||
171 | } |
||
172 | |||
173 | break; |
||
174 | |||
175 | case 3: |
||
176 | // convert mnemonics to hex (bip39) and then base64 for decryption |
||
177 | backupData.encryptedPrimaryMnemonic = EncryptionMnemonic.decode(backupData.encryptedPrimaryMnemonic); |
||
178 | if (usePassword) { |
||
179 | backupData.passwordEncryptedSecretMnemonic = EncryptionMnemonic.decode(backupData.passwordEncryptedSecretMnemonic); |
||
180 | } else { |
||
181 | backupData.encryptedRecoverySecretMnemonic = EncryptionMnemonic.decode(backupData.encryptedRecoverySecretMnemonic); |
||
182 | } |
||
183 | |||
184 | // decrypt encryption secret |
||
185 | if (usePassword) { |
||
186 | secret = Encryption.decrypt(backupData.passwordEncryptedSecretMnemonic, new Buffer(backupData.password)); |
||
187 | } else { |
||
188 | secret = Encryption.decrypt(backupData.encryptedRecoverySecretMnemonic, new Buffer(backupData.recoverySecretDecryptionKey, 'hex')); |
||
189 | } |
||
190 | |||
191 | if (!secret) { |
||
192 | throw new Error("Could not decrypt secret with " + (usePassword ? "password" : "decryption key")); |
||
193 | } |
||
194 | |||
195 | // now finally decrypt the primary seed and convert to buffer (along with backup seed) |
||
196 | primarySeed = Encryption.decrypt(backupData.encryptedPrimaryMnemonic, secret); |
||
197 | if (backupData.backupMnemonic) { |
||
198 | backupSeed = new Buffer(bip39.mnemonicToEntropy(backupData.backupMnemonic), 'hex'); |
||
199 | } |
||
200 | |||
201 | break; |
||
202 | |||
203 | default: |
||
204 | throw new Error('Wrong version [' + backupData.walletVersion + ']'); |
||
205 | } |
||
206 | |||
207 | // convert the primary and backup seeds to private keys (using BIP32) |
||
208 | this.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(primarySeed, this.network); |
||
209 | |||
210 | if (backupSeed) { |
||
211 | this.backupPrivateKey = bitcoin.HDNode.fromSeedBuffer(backupSeed, this.network); |
||
212 | this.backupPublicKey = this.backupPrivateKey.neutered(); |
||
213 | } else { |
||
214 | this.backupPrivateKey = false; |
||
215 | this.backupPublicKey = bitcoin.HDNode.fromBase58(backupData.backupPublicKey, this.network); |
||
216 | } |
||
217 | |||
218 | if (this.settings.logging) { |
||
219 | console.log('using password method: ' + usePassword); |
||
220 | console.log("Primary Prv Key: " + this.primaryPrivateKey.toBase58()); |
||
221 | console.log("Primary Pub Key: " + this.primaryPrivateKey.neutered().toBase58()); |
||
222 | console.log("Backup Prv Key: " + (this.backupPrivateKey ? this.backupPrivateKey.toBase58() : null)); |
||
223 | console.log("Backup Pub Key: " + this.backupPublicKey.toBase58()); |
||
224 | } |
||
225 | }; |
||
226 | |||
680 |
This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.
To learn more about declaring variables in Javascript, see the MDN.